﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Input;
using WPFDataEditors.Classes;

namespace WPFDataEditors.Calculator.Control
{
    [ToolboxItem(true)]
    [ToolboxBitmap(typeof(Calculator), "Calculator.Icon.bmp")]
    public class Calculator:System.Windows.Controls.Control,INotifyPropertyChanged
    {

        #region Fields

        private static readonly Uri ControlThemeuri = new Uri("/WPFDataEditors;component/Calculator/Themes/Generic.xaml",UriKind.Relative);
        private readonly ResourceDictionary _controlThemeStyle = new ResourceDictionary { Source = ControlThemeuri };
        private string _displayText;
        private double? _ans;
        private Operand _lastOperand = Operand.None;
        private bool _first;
        private double? _memory;
        private Visibility _memoryVisible;
        private enum Operand
        {
            None,
            Sum,
            Sub,
            SqrRoot,
            Mul,
            Div,
            Inv,
            PlusMinus,
            Percent,
            Mplus,
            Mminus,
            Mstore,
            Mcall
        }

        #endregion

        #region Constructor

        public Calculator()
        {
            PreviewKeyDown += CalculatorPreviewKeyDown;
            DataContext = this;
            Memory = null;
            MemoryVisible=Visibility.Collapsed;
            DisplayText = "0";
            AddResouces();
       }
        void CalculatorPreviewKeyDown(object sender, KeyEventArgs e)
        {
            var type = String.Empty;
            if (e.Key == Key.NumPad0 || e.Key == Key.D0)
            {
                type = "0";
            }
            else if (e.Key == Key.NumPad1 || e.Key == Key.D1)
            {
                type = "1";
            }
            else if (e.Key == Key.NumPad2 || e.Key == Key.D2)
            {
                type = "2";
            }
            else if (e.Key == Key.NumPad3 || e.Key == Key.D3)
            {
                type = "3";
            }
            else if (e.Key == Key.NumPad4 || e.Key == Key.D4)
            {
                type = "4";
            }
            else if (e.Key == Key.NumPad5 || e.Key == Key.D5)
            {
                type = "5";
            }
            else if (e.Key == Key.NumPad6 || e.Key == Key.D6)
            {
                type = "6";
            }
            else if (e.Key == Key.NumPad7 || e.Key == Key.D7)
            {
                type = "7";
            }
            else if (e.Key == Key.NumPad8 || e.Key == Key.D8)
            {
                type = "8";
            }
            else if (e.Key == Key.NumPad9 || e.Key == Key.D9)
            {
                type = "9";
            }
            ClickCommandHandler(type);
        }

       

        #endregion

        #region Properties


        public double? Memory
        {
            get { return (double?)GetValue(MemoryProperty); }
            set
            {
                SetValue(MemoryProperty, value);
                MemoryVisible = value == null ? Visibility.Collapsed : Visibility.Visible;

            }
        }
        public static readonly DependencyProperty MemoryProperty = DependencyProperty.Register(
          "Memory", typeof(double?), typeof(Calculator), new PropertyMetadata(null));

        public string DisplayText
        {
            get { return (string)GetValue(DisplayTextProperty); }
            set
            {
                if (string.IsNullOrEmpty(value))
                    value = "0";
                if (value == "-")
                    value = "0";
                _displayText = value;
                if (DisplayValueChanged != null)
                {
                    DisplayValueChanged(this, new EventArgs());
                }
                SetValue(DisplayTextProperty, value);

            }
        }
        public static readonly DependencyProperty DisplayTextProperty = DependencyProperty.Register(
          "DisplayText", typeof(string), typeof(Calculator), new PropertyMetadata("0"));


        public Visibility MemoryVisible
        {
            get { return _memoryVisible; }
            private set 
            {
                _memoryVisible = value;
                NotifyPropertyChanged(this, o => o.MemoryVisible);
            }
        }

    

        #endregion

        #region Commands

        RelayCommand _clickCommand;

        public ICommand ClickCommand
        {
            get { return _clickCommand ?? (_clickCommand = new RelayCommand(ClickCommandHandler)); }
        }

        RelayCommand _copyCommand;

        public ICommand CopyCommand
        {
            get { return _copyCommand ?? (_copyCommand = new RelayCommand(CopyCommandHandler,CanCopy)); }
        }

        


        RelayCommand _pasteCommand;

        public ICommand PasteCommand
        {
            get { return _pasteCommand ?? (_pasteCommand = new RelayCommand(PasteCommandHandler,CanPaste)); }
        }

        

        #endregion

        #region CommandHandler

        private void CopyCommandHandler(object param)
        {
            if (!string.IsNullOrEmpty(DisplayText))
            Clipboard.SetText(DisplayText);
        }

        private void PasteCommandHandler(object param)
        {
            if (Clipboard.ContainsText())
            {
                var actualValue = Clipboard.GetText();
                var aftervalue = actualValue.Where(c => c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9' || c == '0').Aggregate(String.Empty, (current, c) => current + c.ToString(CultureInfo.InvariantCulture));
                DisplayText = aftervalue;
            }
                
        }


        private void ClickCommandHandler(object param)
        {
            Focus();
            var buttonType = Convert.ToString(param);
            switch (buttonType)
            {
                case "MC":
                    Memory = null;
                    break;
                case "MR":
                    DisplayText = GetMemory().ToString(CultureInfo.InvariantCulture);
                    _lastOperand=Operand.Mcall;
                    break;
                case "MS":
                    Memory = Convert.ToDouble(DisplayText);
                    _lastOperand=Operand.Mstore;
                    break;
                case "M+":
                    Memory = GetMemory() + Convert.ToDouble(DisplayText);
                    Reset();
                    _lastOperand=Operand.Mplus;
                    break;
                case "M-":
                    Memory = GetMemory() - Convert.ToDouble(DisplayText);
                    Reset();
                    _lastOperand = Operand.Mminus;
                    break;
                case "BackSpace":
                    if(!string.IsNullOrEmpty(DisplayText))
                    {
                        DisplayText = DisplayText.Substring(0, DisplayText.Length - 1);
                    }
                    break;
                case "0":
                case "1":
                case "2":
                case "3":
                case "4":
                case "5":
                case "6":
                case "7":
                case "8":
                case "9":
                    if(_lastOperand==Operand.None)
                    {
                        if (DisplayText != "0")
                            DisplayText = DisplayText + buttonType;
                        else
                            DisplayText = buttonType;
                        break;
                    }
                    if (_lastOperand == Operand.Mplus || _lastOperand == Operand.Mplus || _lastOperand == Operand.Mstore||
                        _lastOperand==Operand.SqrRoot || _lastOperand == Operand.Percent|| _lastOperand == Operand.Inv  || 
                        _lastOperand == Operand.Mcall)
                    {
                        DisplayText = buttonType;
                        Reset();
                    }
                    else
                    {
                        if (_first)
                        {
                            DisplayText = buttonType;
                            _first = false;
                        }
                        else
                        {
                            if (DisplayText != "0")
                                DisplayText = DisplayText + buttonType;
                            else
                                DisplayText = buttonType;
                        }
                    }
                    
                   
                    break;
                case ".":
                    if(!DisplayText.Contains("."))
                    {
                        DisplayText = DisplayText + ".";
                    }
                    break;
                case "C":
                    Reset();
                    DisplayText = "0";
                    break;
                case "CE":
                    DisplayText = "0";
                    break;
                case "+":
                    Calculate(Operand.Sum);
                   break;
                case "-":
                   Calculate(Operand.Sub);
                   break;
                case "/":
                   Calculate(Operand.Div);
                   break;
                case "*":
                   Calculate(Operand.Mul);
                   break;
                case "%":
                   Calculate(Operand.Percent);
                   break;
                case "1/x":
                   Calculate(Operand.Inv);
                   break;
                case "SqrRoot":
                   Calculate(Operand.SqrRoot);
                   break;
                case "PlusMinus":
                   Calculate(Operand.PlusMinus);
                   break;
                case "=":
                   if (_lastOperand == Operand.Mplus || _lastOperand == Operand.Mplus || _lastOperand == Operand.Mstore ||
                      _lastOperand == Operand.SqrRoot || _lastOperand == Operand.Percent || _lastOperand == Operand.Inv
                       || _lastOperand == Operand.Mcall)
                   {
                       return;
                   }
                    Calculate(_lastOperand);
                    Reset();
                    break;
            }
        }

       

        #endregion

        #region Methods

        private bool CanCopy(object obj)
        {
            return !string.IsNullOrEmpty(DisplayText);
        }

        private bool CanPaste(object obj)
        {
            return Clipboard.ContainsText();
        }

        private double GetMemory()
        {
            return Memory == null ? 0 : Memory.Value;
        }

        private void Reset()
        {
            _lastOperand = Operand.None;
            _ans = null;
        }

        private void AddResouces()
        {

            if (!UriParser.IsKnownScheme("pack"))
                UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), "pack", -1);


            //Datagrid
            if (!Resources.MergedDictionaries.Contains(_controlThemeStyle))
                Resources.MergedDictionaries.Add(_controlThemeStyle);
        }

        protected void NotifyPropertyChanged<T, TP>(T source, Expression<Func<T, TP>> pe)
        {
            PropertyChanged.Raise(source, pe);
        }

        private void Calculate(Operand operand)
        {
           
            _first = true;
            if (operand == Operand.SqrRoot || operand == Operand.Inv || operand == Operand.Percent || operand==Operand.PlusMinus)
            {
                if (operand != Operand.PlusMinus && operand != Operand.Percent)
                _ans = GetDisplayValue();

                switch (operand)
                {
                    
                    case Operand.SqrRoot:
                        _ans = Math.Sqrt(GetDisplayValue());
                        break;
                    case Operand.Inv:
                        _ans = 1 / GetDisplayValue();
                        break;
                    case Operand.Percent:
                        if(_ans!=0)
                        {
                            _ans = _ans + (GetDisplayValue()*_ans)/100;
                        }
                        break;
                    case Operand.PlusMinus:
                        DisplayText = (-1 * GetDisplayValue()).ToString(CultureInfo.InvariantCulture);
                        return;
                }
                DisplayText = Convert.ToString(_ans);
                Reset();
                return;
            }
            _lastOperand = operand;
            if (_ans == null)
            {
                _ans = GetDisplayValue();
                return;
            }
            switch (operand)
            {
               case Operand.Sum:
                    _ans= _ans + GetDisplayValue();
                    break;
               case Operand.Sub:
                    _ans = _ans - GetDisplayValue();
                    break;
               case Operand.Mul:
                    _ans = _ans * GetDisplayValue();
                    break;
               case Operand.Div:
                    _ans = _ans / GetDisplayValue();
                    break;
               
            }
            DisplayText = Convert.ToString(_ans);
            _ans = GetDisplayValue();
        }

        private double GetDisplayValue()
        {
            return Convert.ToDouble(DisplayText);
        }
        
        #endregion

        #region Events

        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler DisplayValueChanged;
        
        #endregion
       
    }
}
